#include "SNCascadeBoost.h"
#include "SNCascadeBoostTrainData.h"
#include "opencv2\core\core_c.h"
#include "SNCascadeBoostTree.h"

//-------------------------------------------------------------------------------
SNCascadeBoost::SNCascadeBoost()
{
}
//-------------------------------------------------------------------------------

SNCascadeBoost::~SNCascadeBoost()
{
}
//-------------------------------------------------------------------------------

bool SNCascadeBoost::Train(const SNFeatureEvaluator* feature_evaluator, int num_samples, int precalc_val_buf_size, int precalc_idx_buf_size, const SNCascadeBoostParams& params /*= SNCascadeBoostParams()*/)
{
	bool is_trained = false;

	clear();
	data = new SNCascadeBoostTrainData(feature_evaluator, num_samples, precalc_val_buf_size, precalc_idx_buf_size, params);
	CvMemStorage* storage = cvCreateMemStorage();
	weak = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvBoostTree*), storage);
	storage = 0;

	set_params(params);
	if ((params.boost_type == LOGIT) || (params.boost_type == GENTLE))
		data->do_responses_copy();

	update_weights(0);

	std::cout << "+----+---------+---------+" << std::endl;
	std::cout << "|  N |    HR   |    FA   |" << std::endl;
	std::cout << "+----+---------+---------+" << std::endl;

	do
	{
		SNCascadeBoostTree* tree = new SNCascadeBoostTree;
		if (!tree->train(data, subsample_mask, this))
		{
			delete tree;
			break;
		}
		cvSeqPush(weak, &tree);
		update_weights(tree);
		trim_weights();
		if (cvCountNonZero(subsample_mask) == 0)
			break;
	} 
	while (!IsErrDesired() && (weak->total < params.weak_count));

	if (weak->total > 0)
	{
		data->is_classifier = true;
		data->free_train_data();
		is_trained = true;
	}
	else
	{
		clear();
	}
	
	return is_trained;
}
//-------------------------------------------------------------------------------

float SNCascadeBoost::Predict(int sampleIdx, bool return_sum /*= false*/) const
{
	CV_Assert(weak);
	double sum = 0;
	CvSeqReader reader;
	cvStartReadSeq(weak, &reader);
	cvSetSeqReaderPos(&reader, 0);
	for (int i = 0; i < weak->total; i++)
	{
		CvBoostTree* wtree;
		CV_READ_SEQ_ELEM(wtree, reader);
		//sum += ((SNCascadeBoostTree*)wtree)->predict(sampleIdx)->value;
	}

	if (!return_sum)
		sum = sum < threshold - SN_THRESHOLD_EPS ? 0.0 : 1.0;

	return (float)sum;
}
//-------------------------------------------------------------------------------

bool SNCascadeBoost::SetParams(const CvBoostParams& params)
{
	return false;
}
//-------------------------------------------------------------------------------

void SNCascadeBoost::UpdateWeights(CvBoostTree* tree)
{

}
//-------------------------------------------------------------------------------

bool SNCascadeBoost::IsErrDesired()
{
	int s_count = data->sample_count;
	
	int	num_pos = 0;
	int num_neg = 0;
	int num_false = 0;
	int num_pos_true = 0;

	std::vector<float> eval(s_count);

	for (int i = 0; i < s_count; i++)
	{
		if (((SNCascadeBoostTrainData*)data)->FeatureEvaluator->getCls(i) == 1.0F)
			eval[num_pos++] = Predict(i, true);
	}
	
	SNSortFlt(&eval[0], num_pos, 0);

	int thresholdIdx = (int)((1.0F - MinHitRate) * num_pos);
	threshold = eval[thresholdIdx];
	num_pos_true = num_pos - thresholdIdx;

	for (int i = thresholdIdx - 1; i >= 0; i--)
	{
		if (abs(eval[i] - threshold) < FLT_EPSILON)
			num_pos_true++;
	}

	float hit_rate = ((float)num_pos_true) / ((float)num_pos_true);

	for (int i = 0; i < s_count; i++)
	{
		if (((SNCascadeBoostTrainData*)data)->FeatureEvaluator->getCls(i) == 0.0F)
		{
			num_neg++;
			if (Predict(i))
				num_false++;
		}
	}

	float false_alarm = ((float)num_false) / ((float)num_neg);

	std::cout << "|"; std::cout.width(4); std::cout << std::right << weak->total;
	std::cout << "|"; std::cout.width(9); std::cout << std::right << hit_rate;
	std::cout << "|"; std::cout.width(9); std::cout << std::right << false_alarm;
	std::cout << "|" << std::endl;
	std::cout << "+----+---------+---------+" << std::endl;

	return false_alarm <= MaxFalseAlarm;
}
//-------------------------------------------------------------------------------